home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / msysjour / vol04 / 01b / sleuth / sleuth.c < prev    next >
C/C++ Source or Header  |  1988-11-07  |  27KB  |  841 lines

  1. /*-----------------------------------------------------------------*/
  2. /* Sleuth.c                                                        */
  3. /* Window snooper for Presentation Manager                         */
  4. /*-----------------------------------------------------------------*/
  5.  
  6. #include "MyOs2.h"
  7.  
  8. #undef NULL
  9.  
  10. #include "Machine.h"
  11.  
  12. #include <malloc.h>
  13. #include <stdarg.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17.  
  18. #include "Sleuth.h"
  19.  
  20. /*-----------------------------------------------------------------*/
  21. /*  The display for a window looks like this in collapsed mode:    */
  22. /*                                                                 */
  23. /*  Window HHHH:HHHH [id] {class} (L,B;R,T) "text"                 */
  24. /*                                                                 */
  25. /*  or like this in expanded mode:                                 */
  26. /*                                                                 */
  27. /*      Window handle: HHHH:HHHH   Owner window: HHHH:HHHH         */
  28. /*        Class name: {class name}                                 */
  29. /*        Window text: "text"                                      */
  30. /*        Class style:  HHHHHHHH                                   */
  31. /*        Window style: HHHHHHHH                                   */
  32. /*        Class function: HHHH:HHHH   Window function: HHHH:HHHH   */
  33. /*        Window ID: DDDDD   Process ID: HHHH   Thread ID: HHHH    */
  34. /*        Message queue handle: HHHH   Window lock count: DDDDD    */
  35. /*        Window extra alloc: DDDDD                                */
  36. /*        Window rectangle: Left=D, Bottom=D, Right=D, Top=D       */
  37. /*      {blank line}                                               */
  38. /*                                                                 */
  39. /*  Total number of lines for one window display: 11               */
  40. /*-----------------------------------------------------------------*/
  41.  
  42. #define LINES_PER_WINDOW    11
  43. #define WINDOW_WIDTH        160
  44.  
  45. /*-----------------------------------------------------------------*/
  46. /* Structure of information for each window.                       */
  47. /*-----------------------------------------------------------------*/
  48.  
  49. #define CLASSMAX    40
  50. #define TEXTMAX     40
  51.  
  52. typedef struct {
  53.     HWND    hwnd;                   /* Window handle */
  54.     CLASSINFO class;                /* Class info */
  55.     CHAR    szClass[CLASSMAX];      /* Class name */
  56.     CHAR    szText[TEXTMAX];        /* Window title or contents */
  57.     ULONG   flStyle;                /* WS_ window style */
  58.     HWND    hwndOwner;              /* Owner window handle */
  59.     PFNWP   pfnwp;                  /* Window function */
  60.     USHORT  usWindowID;             /* Window ID */
  61.     PID     ProcessID;              /* Process ID */
  62.     TID     ThreadID;               /* Thread ID */
  63.     HMQ     hmq;                    /* Message queue handle */
  64.     RECTL   rclWindow;              /* Window rect, screen coord. */
  65.     SHORT   sLockCount;             /* Window lock count */
  66.     SHORT   sLevel;                 /* Child window nesting level */
  67.     FLAG    fSelect:1;              /* Is this window selected? */
  68.     FLAG    fHasText:1;             /* Does the window have text? */
  69. } INFO;
  70.  
  71. typedef INFO * PINFO;               /* Pointer to INFO array */
  72.  
  73. /*-----------------------------------------------------------------*/
  74. /* Static variables.                                               */
  75. /*-----------------------------------------------------------------*/
  76.  
  77. PIDINFO     pidi;
  78.  
  79. HAB         hab;
  80. HMQ         hmq;
  81. HWND        hwndDesktop, hwndObject;
  82. HWND        hwndSleuth, hwndFrame, hwndHorzScroll, hwndVertScroll;
  83.  
  84. PINFO       pInfoBase;
  85. SHORT       sWinCount;              /* # of windows in system */
  86. BOOL        fExpand = FALSE;        /* Expanded display mode? */
  87. SHORT       sLinesEach = 1;         /* 1 or LINES_PER_WINDOW */
  88. SHORT       sCharX;                 /* Character width in pixels */
  89. SHORT       sCharY;                 /* Character height in pixels */
  90. SHORT       sDescenderY;            /* Descender height */
  91. SHORT       sExtLeading;            /* Vert. space between chars */
  92. HPS         hpsPaint;               /* PS for SleuthPaint */
  93. POINTL      ptlPaint;               /* Current pos for SleuthPaint */
  94. CHAR        szClass[10];            /* Our window class name */
  95. CHAR        szTitle[40];            /* Our window title */
  96.  
  97. /*-----------------------------------------------------------------*/
  98. /* Function prototypes.                                            */
  99. /*-----------------------------------------------------------------*/
  100.  
  101. VOID                main( VOID );
  102. PSZ                 SleuthClassName( PSZ );
  103. BOOL                SleuthGetAllWindows( VOID );
  104. VOID                SleuthGetWindowTree( HWND hwnd, SHORT sLevel );
  105. BOOL                SleuthInit( VOID );
  106. PSZ                 SleuthMsgName( USHORT );
  107. VOID                SleuthPaint( HWND hwndPaint );
  108. VOID      CDECL     SleuthPaint1( CHAR* szFormat, ... );
  109. VOID                SleuthQueryScrollBar( HWND hwndBar, SHORT* psPos,
  110.                                           SHORT* psMin,
  111.                                           SHORT* psMax );
  112. SHORT               SleuthScroll( HWND hwnd, USHORT msg,
  113.                                   USHORT idBar,
  114.                                   USHORT cmd, SHORT sPos );
  115. VOID                SleuthSetOneBar( HWND hwndBar, SHORT sMax );
  116. VOID                SleuthSetScrollBars( VOID );
  117. VOID                SleuthTerminate( VOID );
  118. MRESULT   EXPENTRY  SleuthWndProc( HWND, USHORT, MPARAM, MPARAM );
  119.  
  120. /*-----------------------------------------------------------------*/
  121. /* Application main program.                                       */
  122. /*-----------------------------------------------------------------*/
  123.  
  124. VOID main()
  125. {
  126.     QMSG        qmsg;
  127.  
  128.     /* Initialize application, quit if any errors */
  129.  
  130.     if( ! SleuthInit() )
  131.       return;
  132.  
  133.     /* Main message processing loop */
  134.  
  135.     while( WinGetMsg( hab, &qmsg, NULL, 0, 0 ) )
  136.       WinDispatchMsg( hab, &qmsg );
  137.  
  138.     /* Application termination */
  139.  
  140.     SleuthTerminate();
  141. }
  142.  
  143. /*-----------------------------------------------------------------*/
  144. /* Convert a class name into its printable form.  Normal class     */
  145. /* names are returned unchanged; the special WC_ "names" are       */
  146. /* converted into text.                                            */
  147. /*-----------------------------------------------------------------*/
  148.  
  149. typedef struct _CLASSNAMES {
  150.     NPSZ        szNum;
  151.     NPSZ        szName;
  152. } CLASSNAMES;
  153.  
  154. typedef CLASSNAMES NEAR * NPCLASSNAMES;
  155.  
  156. CLASSNAMES aClassNames[] = {
  157.     { "#1",  "WC_FRAME"      },
  158.     { "#2",  "WC_DIALOG"     },
  159.     { "#3",  "WC_BUTTON"     },
  160.     { "#4",  "WC_MENU"       },
  161.     { "#5",  "WC_STATIC"     },
  162.     { "#6",  "WC_ENTRYFIELD" },
  163.     { "#7",  "WC_LISTBOX"    },
  164.     { "#8",  "WC_SCROLLBAR"  },
  165.     { "#9",  "WC_TITLEBAR"   },
  166.     { "#10", "WC_SIZEBORDER" },
  167.     { NULL,  NULL            }
  168. };
  169.  
  170. PSZ SleuthClassName( pszClass )
  171.     PSZ         pszClass;
  172. {
  173.     NPCLASSNAMES npNames;
  174.  
  175.     if( pszClass[0] != '#' )
  176.       return pszClass;
  177.  
  178.     for( npNames = &aClassNames[0];  npNames->szNum;  npNames++ )
  179.       if( strcmp( pszClass, npNames->szNum ) == 0 )
  180.         return npNames->szName;
  181.  
  182.     return pszClass;
  183. }
  184.  
  185. /*-----------------------------------------------------------------*/
  186. /* Gather up information on all windows in PM and fill in the      */
  187. /* INFO structure for them.                                        */
  188. /*-----------------------------------------------------------------*/
  189.  
  190. BOOL SleuthGetAllWindows()
  191. {
  192.     sWinCount = 0;
  193.  
  194.     /* Pick up both trees, from hwndDesktop and hwndObject */
  195.  
  196.     SleuthGetWindowTree( hwndDesktop, 0 );
  197.     SleuthGetWindowTree( hwndObject,  0 );
  198.  
  199.     /* Set scroll bars based on new window count */
  200.  
  201.     SleuthSetScrollBars();
  202.  
  203.     /* Force our window to be repainted */
  204.  
  205.     WinInvalidateRect( hwndSleuth, NULL, TRUE );
  206.  
  207.     return TRUE;
  208. }
  209.  
  210. /*-----------------------------------------------------------------*/
  211. /* Gather information on all windows in the tree starting at hwnd, */
  212. /* and add an entry to the INFO array for each one.                */
  213. /*-----------------------------------------------------------------*/
  214.  
  215. VOID SleuthGetWindowTree( hwnd, sLevel )
  216.     HWND        hwnd;
  217.     SHORT       sLevel;
  218. {
  219.     PINFO       pInfo;
  220. /*  HENUM       henum;  */
  221.     HWND        hwndChild;
  222.  
  223.     /* Count the window and allocate an INFO entry */
  224.  
  225.     sWinCount++;
  226.  
  227.     if( ! pInfoBase )
  228.       pInfoBase = malloc( sizeof(INFO) );
  229.     else
  230.       pInfoBase = realloc( pInfoBase, sWinCount*sizeof(INFO) );
  231.  
  232.     if( ! pInfoBase )
  233.       exit( 9 );
  234.  
  235.     pInfo = pInfoBase + sWinCount - 1;  /* -> INFO for this window */
  236.  
  237.     /* Gather up this window's information */
  238.  
  239.     pInfo->hwnd = hwnd;
  240.     pInfo->fSelect = FALSE;
  241.     pInfo->fHasText = FALSE;
  242.     pInfo->class.flClassStyle  = 0L;
  243.     pInfo->class.pfnWindowProc = 0L;
  244.     pInfo->class.cbWindowData  = 0;
  245.  
  246.     pInfo->flStyle = WinQueryWindowULong( hwnd, QWL_STYLE );
  247.     pInfo->hwndOwner = WinQueryWindow( hwnd, QW_OWNER, FALSE );
  248.     pInfo->pfnwp = WinQueryWindowPtr( hwnd, QWP_PFNWP );
  249.     pInfo->usWindowID = WinQueryWindowUShort( hwnd, QWS_ID );
  250.     WinQueryWindowProcess( hwnd, &pInfo->ProcessID,
  251.                                  &pInfo->ThreadID );
  252.     pInfo->hmq = WinQueryWindowPtr( hwnd, QWL_HMQ );
  253.     WinQueryWindowRect( hwnd, &pInfo->rclWindow );
  254.     pInfo->sLockCount = WinQueryWindowLockCount( hwnd );
  255.     pInfo->sLevel = sLevel;
  256.  
  257.     if( hwnd == hwndDesktop )
  258.       strcpy( pInfo->szClass, "WC_DESKTOP" );
  259.     else if( hwnd == hwndObject )
  260.       strcpy( pInfo->szClass, "WC_OBJECT" );
  261.     else
  262.     {
  263.       WinQueryClassName( hwnd, sizeof(pInfo->szClass),
  264.                          pInfo->szClass );
  265.       WinQueryClassInfo( hab, pInfo->szClass, &pInfo->class );
  266.       if( ! WinIsRectEmpty( hab, &pInfo->rclWindow ) )
  267.         WinMapWindowRect( hwnd, WinQueryWindow(hwnd,QW_PARENT,FALSE),
  268.                           &pInfo->rclWindow );
  269.     }
  270.  
  271.     pInfo->szText[0] = '\0';
  272.     if( pInfo->ProcessID == pidi.pid )
  273.       pInfo->fHasText =  /* wrong... */
  274.         !! WinQueryWindowText( hwnd, sizeof(pInfo->szText),
  275.                                pInfo->szText );
  276.  
  277.     /* Recurse through all child windows.  The #if'd out code is the
  278.        "right" way to do this, but it crashed PM in the preliminary
  279.        version I used.  The other code works OK, but doesn't protect
  280.        against windows being destroyed by other threads while we're
  281.        executing! */
  282.  
  283. #if 0
  284.     henum = WinBeginEnumWindows( hwnd );
  285.  
  286.     while( hwndChild = WinGetNextWindow(henum) )
  287.       SleuthGetWindowTree( hwndChild, sLevel+1 );
  288.  
  289.     WinEndEnumWindows( henum );
  290. #else
  291.     for( hwndChild = WinQueryWindow( hwnd, QW_TOP, FALSE );
  292.          hwndChild;
  293.          hwndChild = WinQueryWindow( hwndChild, QW_NEXT, FALSE ) )
  294.       SleuthGetWindowTree( hwndChild, sLevel+1 );
  295. #endif
  296. }
  297.  
  298. /*-----------------------------------------------------------------*/
  299. /* Initialize the application.                                     */
  300. /*-----------------------------------------------------------------*/
  301.  
  302. BOOL SleuthInit()
  303. {
  304.     HDC         hps;
  305.     FONTMETRICS fm;
  306.     SHORT       sScreenX;
  307.     SHORT       sScreenY;
  308.     ULONG       flFrameFlags;
  309.  
  310.     /* Pick up the basic information we need */
  311.  
  312.     DosGetPID( &pidi );
  313.  
  314.     hab = WinInitialize( 0 );
  315.     hmq = WinCreateMsgQueue( hab, 0 );
  316.  
  317.     hwndDesktop = WinQueryDesktopWindow( hab, NULL );
  318.     hwndObject  = WinQueryObjectWindow( hwndDesktop );
  319.  
  320.     sScreenX = (SHORT)WinQuerySysValue( hwndDesktop, SV_CXSCREEN );
  321.     sScreenY = (SHORT)WinQuerySysValue( hwndDesktop, SV_CYSCREEN );
  322.  
  323.     /* Calculate character size for system font */
  324.  
  325.     hps = WinGetPS( hwndDesktop );
  326.  
  327.     GpiQueryFontMetrics( hps, (LONG)sizeof(fm), &fm );
  328.  
  329.     sCharX = (SHORT)fm.lAveCharWidth;
  330.     sCharY = (SHORT)fm.lMaxBaselineExt;
  331.     sDescenderY = (SHORT)fm.lMaxDescender;
  332.  
  333.     WinReleasePS( hps );
  334.  
  335.     /* Load strings from resource file */
  336.  
  337.     WinLoadString( hab, NULL, IDS_CLASS, sizeof(szClass), szClass );
  338.     WinLoadString( hab, NULL, IDS_TITLE, sizeof(szTitle), szTitle );
  339.  
  340.     /* Register our window class and create main window */
  341.  
  342.     WinRegisterClass( hab, szClass, SleuthWndProc, 0L, 0 );
  343.  
  344. /* original ------
  345.  
  346.     flFrameFlags = FCF_STANDARD | FCF_VERTSCROLL | FCF_HORZSCROLL;
  347.     hwndFrame =
  348.       WinCreateStdWindow( hwndDesktop, FS_ICON, &flFrameFlags,
  349.                           szClass, szTitle, 0L, NULL,
  350.                           ID_SLEUTH, &hwndSleuth );
  351.  
  352. ------- original - change made by Charles Petzold */
  353.  
  354. // tonyr - added FCF_SHELLPOSITION for golden master release 
  355. // and fixed WinCreateStdWindow
  356.  
  357.         flFrameFlags = FCF_SIZEBORDER | FCF_TITLEBAR | 
  358.                         FCF_MINMAX | FCF_SYSMENU | FCF_MENU | FCF_ICON | 
  359.                         FCF_VERTSCROLL | FCF_HORZSCROLL | FCF_SHELLPOSITION ;
  360.         hwndFrame =
  361.         WinCreateStdWindow (hwndDesktop, WS_VISIBLE, &flFrameFlags,
  362.                                 szClass, szTitle, 0L, NULL,
  363.                                 ID_SLEUTH, &hwndSleuth) ;
  364.  
  365.     hwndHorzScroll = WinWindowFromID( hwndFrame, FID_HORZSCROLL );
  366.     hwndVertScroll = WinWindowFromID( hwndFrame, FID_VERTSCROLL );
  367.  
  368.     /* Set the window position.  Change the #if 1 to #if 0 for more
  369.        convenient debugging under Lightspeed's debugger.  This will
  370.        put our window in the top part of the screen, up above the
  371.        debugger windows. */
  372.  
  373. #if 1
  374.     WinSetWindowPos( hwndFrame, NULL,
  375.                      sScreenX *  1 / 20,     /* X: 5% from left */
  376.                      sScreenY *  2 / 10,     /* Y  20% from bottom */
  377.                      sScreenX *  9 / 10,     /* nWidth: 90% */
  378.                      sScreenY *  7 / 10,     /* nHeight: 70% */
  379.                      SWP_MOVE | SWP_SIZE );
  380. #else
  381.     WinSetWindowPos( hwndFrame, NULL,
  382.                      sScreenX *  1 / 10,     /* X: 10% from left */
  383.                      sScreenY *  6 / 10,     /* Y  60% from bottom */
  384.                      sScreenX *  8 / 10,     /* nWidth: 80% */
  385.                      sScreenY *  3 / 10,     /* nHeight: 30% */
  386.                      SWP_MOVE | SWP_SIZE );
  387. #endif
  388.  
  389.     /* Make our window visible now, so it's included in the list */
  390.  
  391.     WinShowWindow( hwndFrame, TRUE );
  392.  
  393.     /* Post a message to ourself to trigger the first  display */
  394.  
  395.     WinPostMsg( hwndSleuth, WM_COMMAND, MPFROMSHORT(CMD_LOOK), 0L );
  396.  
  397.     return TRUE;
  398. //  return FALSE;
  399. }
  400.  
  401. /*-----------------------------------------------------------------*/
  402. /* Paint our window.                                               */
  403. /*-----------------------------------------------------------------*/
  404.  
  405. VOID SleuthPaint( hwnd )
  406.     HWND        hwnd;
  407. {
  408.     SHORT       sWin;
  409.     SHORT       X;
  410.     SHORT       sScrollY, sScrollX;
  411.     RECTL       rclPaint, rclClient;
  412.     PINFO       pInfo;
  413.     CHAR        szQuote[2];
  414.  
  415.     /* Get the PS and erase it */
  416.  
  417.     hpsPaint = WinBeginPaint( hwnd, NULL, &rclPaint );
  418.  
  419.     GpiErase( hpsPaint );
  420.  
  421.     /* Find out how big the window is and how it's scrolled */
  422.  
  423.     WinQueryWindowRect( hwnd, &rclClient );
  424.     sScrollX =
  425.       (SHORT)WinSendMsg( hwndHorzScroll, SBM_QUERYPOS, 0, 0 );
  426.     sScrollY =
  427.       (SHORT)WinSendMsg( hwndVertScroll, SBM_QUERYPOS, 0, 0 );
  428.  
  429.     /* Calculate horizontal paint pos from scroll bar pos */
  430.  
  431.     X = /* ( 1 */ - sScrollX /* ) */ * sCharX;
  432.  
  433.     /* Calculate index into INFO array and vertical paint pos,
  434.        from scroll bar pos and top of painting rectangle */
  435.  
  436.     sWin =
  437.       ( ( (SHORT)rclClient.yTop - (SHORT)rclPaint.yTop ) / sCharY
  438.         + sScrollY )
  439.       / sLinesEach;
  440.  
  441.     ptlPaint.y =
  442.       (SHORT)rclClient.yTop + sDescenderY
  443.         - ( sWin * sLinesEach - sScrollY + 1 ) * sCharY;
  444.  
  445.     pInfo = pInfoBase + sWin;
  446.  
  447.     /* Loop through and paint each entry */
  448.  
  449.     while( sWin < sWinCount  &&
  450.            (SHORT)ptlPaint.y + sCharY >= (SHORT)rclPaint.yBottom )
  451.     {
  452.       /* Set X position and indent child windows */
  453.  
  454.       ptlPaint.x = X + pInfo->sLevel * sCharX * (fExpand ? 4 : 2);
  455.  
  456.       szQuote[0] = szQuote[1] = '\0';
  457.       if( pInfo->fHasText )
  458.         szQuote[0] = '"';
  459.  
  460.       if( ! fExpand )
  461.       {
  462.         /* Paint the one-liner */
  463.  
  464.         SleuthPaint1(
  465.           "%08lX [%04X] {%s} (%d,%d;%d,%d) %s%s%s",
  466.           pInfo->hwnd,
  467.           pInfo->usWindowID,
  468.           SleuthClassName( pInfo->szClass ),
  469.           (INT)pInfo->rclWindow.xLeft,
  470.           (INT)pInfo->rclWindow.yBottom,
  471.           (INT)pInfo->rclWindow.xRight,
  472.           (INT)pInfo->rclWindow.yTop,
  473.           szQuote, pInfo->szText, szQuote
  474.         );
  475.       }
  476.       else
  477.       {
  478. #if 0
  479.         /* Paint the expanded form, first the window handle */
  480.  
  481.         Paint(
  482.           "%s handle: %04X",
  483.           pTypeName,
  484.           lpInfo->winHWnd
  485.         );
  486.  
  487.         /* Paint the rest of the info, indented two spaces more */
  488.  
  489.         sPaintX += sCharX * 2;
  490.  
  491.         Paint( "Class name: %Fs", lpInfo->winClass );
  492.         Paint( "Window title: %Fs", lpInfo->winTitle );
  493.         Paint( "Parent window handle: %04X", lpInfo->winHWndParent );
  494.         Paint(
  495.           "Class function, Window function: %p, %p",
  496.           lpInfo->winClassProc,
  497.           lpInfo->winWndProc
  498.         );
  499.         Paint(
  500.           "Class module handle, Window instance handle: %04X, %04X",
  501.           lpInfo->winClassModule,
  502.           lpInfo->winInstance
  503.         );
  504.         Paint(
  505.           "Class extra alloc, Window extra alloc: %d, %d",
  506.           lpInfo->winClsExtra,
  507.           lpInfo->winWndExtra
  508.         );
  509.         Paint(
  510.           "Class style, Window style: %04X, %08lX",
  511.           lpInfo->winClassStyle,
  512.           lpInfo->winStyle
  513.         );
  514.         Paint(
  515.           lpInfo->winStyle & WS_CHILD ?  "Control ID: %d" :
  516.                                          "Menu handle: %04X",
  517.           lpInfo->winControlID
  518.         );
  519.         Paint(
  520.           "Brush, Cursor, Icon handles: %04X, %04X, %04X",
  521.           lpInfo->winBkgdBrush,
  522.           lpInfo->winCursor,
  523.           lpInfo->winIcon
  524.         );
  525.         Paint(
  526.           "Window rect: Left=%4d, Top=%4d, Right=%4d, Bottom=%4d",
  527.           lpInfo->winWindowRect.left,
  528.           lpInfo->winWindowRect.top,
  529.           lpInfo->winWindowRect.right,
  530.           lpInfo->winWindowRect.bottom
  531.         );
  532.         Paint(
  533.           "Client rect: Left=%4d, Top=%4d, Right=%4d, Bottom=%4d",
  534.           lpInfo->winClientRect.left,
  535.           lpInfo->winClientRect.top,
  536.           lpInfo->winClientRect.right,
  537.           lpInfo->winClientRect.bottom
  538.         );
  539.  
  540.         /* Make a blank line */
  541.  
  542.         sPaintY -= sCharY;
  543. #endif
  544.       }
  545.  
  546.       /* Increment to next INFO entry */
  547.       sWin++;
  548.       pInfo++;
  549.     }
  550.  
  551.     WinEndPaint( hpsPaint );
  552. }
  553.  
  554. /*-----------------------------------------------------------------*/
  555. /* Paint one line of text, using the global variables hpsPaint and */
  556. /* ptlPaint.  The #ifdef PM_MACINTOSH is because Lightspeed C      */
  557. /* doesn't like the ... notation, and Microsoft C doesn't like to  */
  558. /* do without it!                                                  */
  559. /*-----------------------------------------------------------------*/
  560.  
  561. #ifdef PM_MACINTOSH
  562. VOID CDECL SleuthPaint1( szFormat )
  563. #else
  564. VOID CDECL SleuthPaint1( szFormat, ... )
  565. #endif
  566.     CHAR *      szFormat;
  567. {
  568.     va_list     pArgs;
  569.     CHAR        szBuf[160];
  570.  
  571.     va_start( pArgs, szFormat );
  572.  
  573.     GpiCharStringAt(
  574.       hpsPaint, &ptlPaint,
  575.       (LONG)vsprintf( szBuf, szFormat, pArgs ),
  576.       szBuf
  577.     );
  578.  
  579.     va_end( pArgs );
  580.  
  581.     ptlPaint.y -= sCharY;
  582. }
  583.  
  584. /*-----------------------------------------------------------------*/
  585. /* Get a scroll bar's range and position.  More convenient than    */
  586. /* sending the messages every time.                                */
  587. /*-----------------------------------------------------------------*/
  588.  
  589. VOID SleuthQueryScrollBar( hwndBar, psPos, psMin, psMax )
  590.     HWND        hwndBar;
  591.     SHORT*      psPos;
  592.     SHORT*      psMin;
  593.     SHORT*      psMax;
  594. {
  595.     MRESULT     mrRange;
  596.  
  597.     *psPos  = (SHORT)WinSendMsg( hwndBar, SBM_QUERYPOS, 0, 0 );
  598.  
  599.     mrRange = WinSendMsg( hwndBar, SBM_QUERYRANGE, 0, 0 );
  600.     *psMin = SHORT1FROMMR(mrRange);
  601.     *psMax = SHORT2FROMMR(mrRange);
  602. }
  603.  
  604. /*-----------------------------------------------------------------*/
  605. /* Scroll hwnd and adjust idBar according to cmd and sPos.         */
  606. /*-----------------------------------------------------------------*/
  607.  
  608. SHORT SleuthScroll( hwnd, msg, idBar, cmd, sPos )
  609.     HWND        hwnd;
  610.     USHORT      msg;
  611.     USHORT      idBar;
  612.     USHORT      cmd;
  613.     SHORT       sPos;
  614. {
  615.     HWND        hwndBar;
  616.     SHORT       sOldPos;
  617.     SHORT       sDiff;
  618.     SHORT       sMin;
  619.     SHORT       sMax;
  620.     SHORT       sPageSize;
  621.     RECTL       rcl;
  622.  
  623.     /* Get old scroll position and scroll range */
  624.  
  625.     hwndBar =
  626.       WinWindowFromID( WinQueryWindow(hwnd,QW_PARENT,FALSE), idBar );
  627.  
  628.     SleuthQueryScrollBar( hwndBar, &sOldPos, &sMin, &sMax );
  629.  
  630.     /* Calculate page size, horizontal or vertical as needed */
  631.  
  632.     WinQueryWindowRect( hwnd, &rcl );
  633.  
  634.     if( msg == WM_HSCROLL )
  635.       sPageSize = ( (SHORT)rcl.xRight - (SHORT)rcl.xLeft) / sCharX;
  636.     else
  637.       sPageSize = ( (SHORT)rcl.yTop - (SHORT)rcl.yBottom) / sCharY;
  638.  
  639.     /* Select the amount to scroll by, based on the scroll message */
  640.  
  641.     switch( cmd )
  642.     {
  643.       case SB_LINEUP:
  644.         sDiff = -1;
  645.         break;
  646.  
  647.       case SB_LINEDOWN:
  648.         sDiff = 1;
  649.         break;
  650.  
  651.       case SB_PAGEUP:
  652.         sDiff = -sPageSize;
  653.         break;
  654.  
  655.       case SB_PAGEDOWN:
  656.         sDiff = sPageSize;
  657.         break;
  658.  
  659.       case SB_SLIDERPOSITION:
  660.         sDiff = sPos - sOldPos;
  661.         break;
  662.  
  663.       case SBX_TOP:
  664.         sDiff = -30000;  /* Kind of a kludge but it works... */
  665.         break;
  666.  
  667.       case SBX_BOTTOM:
  668.         sDiff = 30000;
  669.         break;
  670.  
  671.       default:
  672.         return 0;
  673.     }
  674.  
  675.     /* Limit scroll destination to sMin..sMax */
  676.  
  677.     if( sDiff < sMin - sOldPos )
  678.       sDiff = sMin - sOldPos;
  679.  
  680.     if( sDiff > sMax - sOldPos )
  681.       sDiff = sMax - sOldPos;
  682.  
  683.     /* Return if net effect is nothing */
  684.  
  685.     if( sDiff == 0 )
  686.       return 0;
  687.  
  688.     /* Set the new scroll bar position and scroll the window */
  689.  
  690.     WinSendMsg( hwndBar, SBM_SETPOS, MRFROMSHORT(sOldPos+sDiff), 0 );
  691.  
  692.     WinScrollWindow(
  693.       hwnd,
  694.       msg == WM_HSCROLL ?  -sDiff*sCharX : 0,
  695.       msg == WM_VSCROLL ?   sDiff*sCharY : 0,
  696.       NULL, NULL, NULL, NULL,
  697.       SW_INVALIDATERGN
  698.     );
  699.  
  700.     /* Force an immediate update for cleaner appearance */
  701.  
  702.     WinUpdateWindow( hwnd );
  703.  
  704.     return sDiff;
  705. }
  706.  
  707. /*-----------------------------------------------------------------*/
  708. /* Set one scroll bar's position and range.                        */
  709. /*-----------------------------------------------------------------*/
  710.  
  711. VOID SleuthSetOneBar( hwndBar, sMax )
  712.     HWND        hwndBar;
  713.     SHORT       sMax;
  714. {
  715.     SHORT       sPos, sOldMin, sOldMax;
  716.  
  717.     SleuthQueryScrollBar( hwndBar, &sPos, &sOldMin, &sOldMax );
  718.  
  719.     if( sMax <= 0 )
  720.       sMax = sPos = 0;
  721.  
  722.     if( sMax != sOldMax )
  723.     {
  724.       WinSendMsg( hwndBar, SBM_SETSCROLLBAR,
  725.                   MPFROMSHORT(sPos), MPFROM2SHORT(0,sMax) );
  726.  
  727.       WinEnableWindow( hwndBar, !!(sMax) );
  728.     }
  729. }
  730.  
  731. /*-----------------------------------------------------------------*/
  732. /* Set both scroll bars according to the window size and the       */
  733. /* number of INFO entries.                                         */
  734. /*-----------------------------------------------------------------*/
  735.  
  736. VOID SleuthSetScrollBars()
  737. {
  738.     RECTL       rcl;
  739.  
  740.     WinQueryWindowRect( hwndSleuth, &rcl );
  741.  
  742.     SleuthSetOneBar( hwndHorzScroll,
  743.                      WINDOW_WIDTH - (SHORT)rcl.xRight / sCharX );
  744.  
  745.     SleuthSetOneBar( hwndVertScroll,
  746.                      sWinCount*sLinesEach - (SHORT)rcl.yTop/sCharY );
  747. }
  748.  
  749. /*-----------------------------------------------------------------*/
  750. /* Terminate the application.                                      */
  751. /*-----------------------------------------------------------------*/
  752.  
  753. VOID SleuthTerminate()
  754. {
  755.     WinDestroyWindow( hwndFrame );
  756.     WinDestroyMsgQueue( hmq );
  757.     WinTerminate( hab );
  758.  
  759.     exit( 0 );
  760. }
  761.  
  762. /*-----------------------------------------------------------------*/
  763. /* Window function for Sleuth's main window.                       */
  764. /*-----------------------------------------------------------------*/
  765.  
  766. MRESULT EXPENTRY SleuthWndProc( hwnd, msg, mp1, mp2 )
  767.     HWND          hwnd;
  768.     USHORT        msg;
  769.     MPARAM        mp1;
  770.     MPARAM        mp2;
  771. {
  772.     switch( msg )
  773.     {
  774.       /* Tell PM that we don't need no stinkin' upside down
  775.          coordinates! */
  776.  
  777.       case WM_CALCVALIDRECTS:
  778.         return MRFROMSHORT( CVR_ALIGNLEFT | CVR_ALIGNTOP );
  779.  
  780.       /* Menu command message - process the command */
  781.  
  782.       case WM_COMMAND:
  783.         switch( COMMANDMSG(&msg)->cmd )
  784.         {
  785.           case CMD_ABOUT:
  786.             return 0L;
  787.  
  788.           case CMD_EXIT:
  789.             WinPostMsg( hwnd, WM_QUIT, 0L, 0L );
  790.             return 0L;
  791.  
  792.           case CMD_LOOK:
  793.             SleuthGetAllWindows();
  794.             return 0L;
  795.         }
  796.         return 0L;
  797.  
  798.         /* Scroll messages - scroll the window */
  799.  
  800.         case WM_HSCROLL:
  801.         case WM_VSCROLL:
  802.           SleuthScroll( hwnd, msg, SHORT1FROMMP(mp1),
  803.                         SHORT2FROMMP(mp2), SHORT1FROMMP(mp2) );
  804.           return 0L;
  805.  
  806.         /* Key-down message - handle cursor keys, ignore others */
  807.  
  808.         case WM_CHAR:
  809.           switch( CHARMSG(&msg)->vkey )
  810.           {
  811.             case VK_LEFT:
  812.             case VK_RIGHT:
  813.               return WinSendMsg( hwndHorzScroll, msg, mp1, mp2 );
  814.             case VK_UP:
  815.             case VK_DOWN:
  816.             case VK_PAGEUP:
  817.             case VK_PAGEDOWN:
  818.               return WinSendMsg( hwndVertScroll, msg, mp1, mp2 );
  819.           }
  820.           return 0L;
  821.  
  822.         /* Paint message - repaint all or part of our window */
  823.  
  824.         case WM_PAINT:
  825.           SleuthPaint( hwnd );
  826.           return 0L;
  827.  
  828.         /* Size message - recalculate our scroll bars */
  829.  
  830.         case WM_SIZE:
  831.           SleuthSetScrollBars();
  832.           return 0L;
  833.     }
  834.  
  835.     /* All other messages go to DefWindowProc */
  836.  
  837.     return WinDefWindowProc( hwnd, msg, mp1, mp2 );
  838. }
  839.  
  840. /*-----------------------------------------------------------------*/
  841.